--Function accepts the dimensions of the grid, the fill value, and whether or not to call the cleanMap() function.
--Fill value is inversely related to how 'closed off' the map feels.
--A low value creates a lot of tunnels and a high value creates more open space.

--For the map itself, it is a relatively small grid (aiming for about 32x18 right now). This is done
--to keep pathfinding for the enemy quick.
--A value of '0' is an open grid space. '1' is a filled in grid space. '2' is the player. '3' is the enemy'.
--NOTE: The player and enemy will move independant of the grid. The grid position is only for pathfinding purposes.
--The grid position will snap to the closest possible location to the actual player and enemy positions.
function createMap(x,y,f, clean)
  --Using the Jumper library to handle pathfinding to ensure the player and enemy can reach eachother.
  Grid = require('jumper.grid')
  Pathfinder = require('jumper.pathfinder')
  --This prevents diagonal pathfinding
  Pathfinder:setMode('ORTHOGONAL')
  
  --numTot is how many total tries it takes to make the map
  --In order to ensure that the game doesn't get stuck in an infinite loop of failed maps,
  --if it takes more than 2000 tries (about half a second or so on my computer), the map is opened
  --up by one notch. This has a minimal asthetic difference, but drastically improves performance.
  num = 0
  numTot = 0

  map = nil
  map = {}  
  
  foundPath = false
  --As long as we don't have a successful map, keep making more.
  while not foundPath do
    --'e' and 'p' are the enemy and player positions in the map. 'x' and 'y' are just the size of the map again
    e,p = mapLoop(x,y,f)
    --Use pathfinding to determine if the player and enemy can reach each other on this map.
    checkPath(e,p,x,y)
  end
  
  --Once we have a successful map, fill in any unreachable gaps. This is purely asthetic and also intended to avoid
  --misleading the player into believing they can go somewhere they can't actually reach.
  if clean then
  cleanMap(x,y)
  end
  
  --Return the created map and player grid positions. Haven't started on enemy yet so no need to return that value yet,
  --but it will be returned as well.
  return map,px,py,ex,ey
end
function checkPath(e,p,x,y)
  --Set up pathfinding
  local grid = Grid(map)
  local myFinder = Pathfinder(grid, 'JPS', 0)
  --Pathfinding tends to be unstable if you try to directly connect the player and enemy, so I use the middle point
  --on the map as an intermediate area. If pathfinding fails, it will return a 'nil' value.
  local ppath = myFinder:getPath(p[1],p[2],x/2,y/2, false)
  local epath = myFinder:getPath(e[1],e[2],x/2,y/2, false)
  --If player and enemy can reach each other, break the loop of creating maps.
  if ppath and epath then
    foundPath = true
    --For debugging purposes. This tells us how many tries it to to make the map
    print('Number of tries: '..numTot)
  else
    --If the map is invalid, go make another one.
    foundPath = false
  end
end
function mapLoop(x,y,fill)
  num = num + 1
  numTot = numTot + 1
  if num > 2000 then
    fill = fill + 1
    num = 0
    print('Taking too long. Increasing fill size.')
  end
  
  --Maps with a fill size smaller than 4 are difficult to create in a reasonable amount of time. So we clamp that
  --as the minimum value regardless of what is sent to the createMap() function.
  if fill <= 4 then
    fill = 4
  end
  
  --Clear any previously created map
  map = nil
  map = {}
  --Create base map with a border along the outer edge
  for i = 1, x, 1 do
    map[i] = {}
    for j = 1, y, 1 do
      if i == 1 or i == x or j == 1 or j == y then
        map[i][j] = 1
      else
        map[i][j] = 0
      end
      
    end
  end
  
  --Randomly place player along left edge, with some horizontal variance
  py = math.random(2,y-1)
  px = math.random(2,4)
  map[px][py] = 2
  local p = {py,px}
  
  --Randomly place enemy along right edge, with some horizontal variance
  ey = math.random(2,y-1)
  ex = math.random(x-3,x-1)
  map[ex][ey] = 3
  local e = {ey,ex}

  
  --Randomly fill map with obstacles
  for i = 1, x, 1 do
    for j = 1, y, 1 do
      --Only look at grid spaces not being used by the player or enemy
      if map[i][j] == 0 then
        local m = math.random(0,fill)
        --Using a fill value of 4, the 'tightest' map possible, this means that there is a 60% chance
        --that any given map space will be an obstacle. Since this is a fixed subtraction, higher fill
        --numbers create more open maps
        if (m == fill or m == fill - 1 or m == fill - 2) and i ~= x/2 and j ~= y/2 then
          map[i][j] = 1
        end
      end
    end
  end
  
  map[px][py] = 0
  map[ex][ey] = 0
  
  return p,e
  
end
function cleanMap(x,y)
  --If an open grid space does not have a path to the player, fill it in.
  for i = 1, x, 1 do
    for j = 1, y, 1 do
      if map[i][j] == 0 then
        local grid = Grid(map)
        local myFinder = Pathfinder(grid, 'JPS', 0)
        local path = myFinder:getPath(p[1],p[2],j,i, false)   
        if path then
        else
          map[i][j] = 1
        end
      end
    end
  end
  print('Gaps are now filled.')
end
